#include "fs.h"

typedef struct
{
  char *name;
  size_t size;
  off_t disk_offset;
  off_t open_offset;
} Finfo;

enum
{
  FD_STDIN,
  FD_STDOUT,
  FD_STDERR,
  FD_FB,
  FD_EVENTS,
  FD_DISPINFO,
  FD_NORMAL
};

/* This is the information about all files in disk. */
static Finfo file_table[] __attribute__((used)) = {
    {"stdin (note that this is not the actual stdin)", 0, 0},
    {"stdout (note that this is not the actual stdout)", 0, 0},
    {"stderr (note that this is not the actual stderr)", 0, 0},
    [FD_FB] = {"/dev/fb", 0, 0},
    [FD_EVENTS] = {"/dev/events", 0, 0},
    [FD_DISPINFO] = {"/proc/dispinfo", 128, 0},
#include "files.h"
};

#define NR_FILES (sizeof(file_table) / sizeof(file_table[0]))

int fs_open(const char *pathname, int flags, int mode)
{
  for (int i = 0; i < NR_FILES; i++)
  {
    if (!strcmp(pathname, file_table[i].name))
    {
      file_table[i].open_offset = 0;
      return i;
    }
  }
  panic("No such file: %s! (in fs_open)\n", pathname);
  return -1;
}

void ramdisk_read(void *buf, off_t offset, size_t len);
ssize_t fs_read(int fd, void *buf, size_t len)
{
  ssize_t delta_len = file_table[fd].size - file_table[fd].open_offset;
  ssize_t read_len = delta_len < len ? delta_len : len;
  switch (fd)
  {
  case FD_DISPINFO:
    dispinfo_read(buf, file_table[fd].open_offset, len);
    break;
  case FD_EVENTS:
    return events_read(buf, len);
  default:
    if (fd < 6 || fd >= NR_FILES)
      return -1;
    ramdisk_read(buf, file_table[fd].disk_offset + file_table[fd].open_offset, read_len);
    break;
  }
  file_table[fd].open_offset += read_len;
  return read_len;
}

void ramdisk_write(const void *buf, off_t offset, size_t len);
ssize_t fs_write(int fd, const void *buf, size_t len)
{
  ssize_t delta_len = file_table[fd].size - file_table[fd].open_offset;
  ssize_t write_len = delta_len < len ? delta_len : len;
  size_t i = 0;
  switch (fd)
  {
  case FD_STDIN:
    return -1;
  case FD_STDOUT:
  case FD_STDERR:
    while (i++ < len)
      _putc(*(char *)buf++);
    return len;
  case FD_FB:
    fb_write(buf, file_table[fd].open_offset, len);
    break;
  default:
    if (fd < 6 || fd >= NR_FILES)
      return -1;
    ramdisk_write(buf, file_table[fd].disk_offset + file_table[fd].open_offset, write_len);
    break;
  }
  file_table[fd].open_offset += write_len;
  return write_len;
}

off_t fs_lseek(int fd, off_t offset, int whence)
{
  if (whence == SEEK_SET)
    file_table[fd].open_offset = offset;
  else if (whence == SEEK_CUR)
    file_table[fd].open_offset += offset;
  else if (whence == SEEK_END)
    file_table[fd].open_offset = file_table[fd].size + offset;
  else
    panic("Invalid whence! (in fs_lseek)");
  return file_table[fd].open_offset;
}

int fs_close(int fd)
{
  return 0;
}

size_t fs_filesz(int fd)
{
  return file_table[fd].size;
}

void init_fs()
{
  int w = _screen.width;
  int h = _screen.height;
  file_table[FD_FB].size = w * h * sizeof(uint32_t);
  file_table[FD_FB].open_offset = 0;
}
